home *** CD-ROM | disk | FTP | other *** search
/ The Business Master (3rd Edition) / The Business Master (3rd Edition).iso / files / wordmisc / pcspell / speller2.pa4 < prev    next >
Text File  |  1988-03-29  |  13KB  |  350 lines

  1. {$N-}    {No numeric coprocessor}
  2. {$S-}
  3. {$V-}
  4.  
  5. PROGRAM SPELLER2;  { SPELL CHECKER -- with cmd line }
  6.  
  7. {   This spell checker is based on the ideas contained in PC-SPELL ver
  8.     1.15 in BASIC by Andy Wildenberg. In that program the text file is
  9.     read into memory and put into a list of words in a string array. The
  10.     string array is then sorted and the unique words removed into
  11.     another array. Thus a unique word array is formed which is in
  12.     alphabetical order. This word list is then compared to a dictionary
  13.     file which is an ASCII list of legal words also in alphabetical
  14.     order. If the word is not found then it is placed into a file of
  15.     possible misspelled words on disk. The user is then responsible for
  16.     printing the list of misspelled words and using a global change
  17.     feature in a word processor to find and replace the words with the
  18.     correct spelling.
  19.  
  20.     This spell checker works in much the same way except that a unique
  21.     word file is formed in an array alphabetically as the text file is
  22.     parsed into words. The rest of the process is about the same.
  23.  
  24.     To use, just type the name of the program followed by a parameter
  25.     specifying the file. The parameter is optional and if ommitted then the
  26.     program will request this name.
  27.  
  28.     Version SPELLER2 is compatible with WINDOWS facilities and adds a alternate
  29.     dictionary file as an optional second parameter on the command line. This
  30.     version has been converted to compile with Turbo Pascal version 4.x.
  31.  
  32.     J. Leeson, March 29, 1988
  33. }
  34. { *************************************************************************}
  35.  
  36. CONST
  37.     WORDSIZE : integer = 16;
  38.  
  39. TYPE
  40.     STRPARAM = string;
  41.     WORDTYP = string [16];
  42.     WORDPTR = ^WORDTYP;
  43.     PTRARRAY = array [0..4000] of WORDPTR; {Limited to 8191 because the
  44.                                  Move function requires an integer parameter
  45.                                  for length in bytes of data to move. SPELLERW
  46.                                  reduced to 4000 to reduce memory requirements.}
  47.  
  48. VAR
  49.     SRCNAME : string;  { Name of source file to spell check }
  50.     ALTDICNAME : string; { Name of the alternate dictionary file }
  51.     OPPATH : string;   { DOS path for speller files }
  52.     OPNAME : string;   { DOS name for speller files }
  53.     OUTNAME : string;  { Name of output file ( default srcfile.MIS) }
  54.     DOCWORDCNT, UNIQUECNT, MISSPELLCNT : integer;
  55.     I : integer;
  56.     WORDINDX : PTRARRAY;
  57.     A_WORD, ALTWORD, TEMP1 : WORDTYP;
  58.     PREFIX : string [1];
  59.     MATCH, ALTDIC : boolean;
  60.     SRCFILE, DICFILE, ALTDICFILE, MISSFILE : text;
  61.     ABuf, SBuf : array[0..$fff] of char; {buffers for source and altdic}
  62.     DBuf : array[0..$1fff] of char;  {buffer for dictionary file}
  63.     x : byte;
  64.     PATHSTRING : string; { working storage for path strings }
  65.  
  66. FUNCTION LOWCASE (var A : char) : boolean;
  67. { *************************************************************************}
  68. {   LOWCASE modifies the character parameter "A" to make it a lower case
  69.     alpha character if it is an upper case alpha. If the character
  70.     parameter is alpha ('a'..'z' or 'A'..'Z') then the function returns
  71.     TRUE else it returns FALSE. }
  72. { *************************************************************************}
  73. var x : byte;
  74.  
  75. begin
  76.     x := ord (A);
  77.     if (x>96) and (x<123) then LOWCASE := true
  78.     else begin
  79.         if (x>64) and (x<91) then
  80.         begin
  81.            A := chr (x+32);
  82.            LOWCASE := true;
  83.         end
  84.         else LOWCASE := false;
  85.     end;
  86. end; { of LOWCASE }
  87.  
  88. PROCEDURE GETWORD (var FILNAME : text; var A_WORD : WORDTYP);
  89. { *************************************************************************}
  90. {GETWORD version 1.2. Defines the start of a word as the next alpha
  91. character in the file. A word is formed by adding characters until a
  92. non-alpha character is found. Contractions are accepted as identified by
  93. a single quote followed by an alpha character occuring after the SOW.
  94. Upper case letters are converted to lower case.}
  95. { *************************************************************************}
  96. VAR
  97.     CH, CH2 : char;
  98.     SOW : boolean;
  99.     {Global WORDSIZE = maximum word length value.}
  100. begin
  101.     SOW := false;
  102.     A_WORD := '';
  103.     repeat
  104.        read (FILNAME, CH);
  105.        if LOWCASE (CH) then SOW := true
  106.     until SOW or eof (FILNAME);
  107.     if SOW then
  108.     begin
  109.        A_WORD := CH;
  110.        repeat
  111.           read (FILNAME, CH);
  112.           if LOWCASE (CH) then
  113.           begin
  114.              if Length (A_WORD) < WORDSIZE then A_WORD := A_WORD + CH
  115.              else SOW := false;
  116.           end
  117.           else begin
  118.              if CH <> '''' then SOW := false
  119.              else begin
  120.                 if not Eof (FILNAME) then
  121.                 begin
  122.                    Read (FILNAME, CH2);
  123.                    if LOWCASE (CH2) then
  124.                    begin
  125.                       if Length (A_WORD) < WORDSIZE-1 then
  126.                          A_WORD := A_WORD + CH + CH2 else SOW := false;
  127.                    end
  128.                    else SOW := false;
  129.                 end;
  130.              end;
  131.           end;
  132.        until (not SOW) or eof (FILNAME);
  133.     end;
  134. end; { of GETWORD }
  135.  
  136. procedure ADDUNIQUE (var LIST : PTRARRAY; A_WORD : WORDTYP; var TOP : integer);
  137. { ***************************************************************************}
  138. { This procedure does a binary search of the LIST looking for the location
  139.   where A_WORD belongs. Once it finds the place, if A_WORD is there then it 
  140.   exits.  If not, then it moves the list up by one pointer and puts the new
  141.   word there.}
  142. { ***************************************************************************}
  143. var
  144.     SEARCH : boolean;
  145.     MID, LOW, HIGH, COUNT : integer;
  146.  
  147. begin
  148.     SEARCH := true;
  149.     LOW := 0; MID := Trunc (TOP/2); HIGH := TOP;
  150.     while SEARCH do               {** Find the place where A_WORD belongs. **}
  151.     begin
  152.        if MID = LOW then SEARCH := false
  153.        else begin
  154.           if A_WORD < LIST [MID]^ then HIGH := MID
  155.           else LOW := MID;            {** A_WORD is >= word at LIST [MID]^ **}
  156.           MID := LOW + Trunc ((HIGH-LOW)/2);
  157.        end;
  158.     end; {** of SEARCH. MID is at the location containing A_WORD or else
  159.               A_WORD goes at the location after MID. **}
  160.     if A_WORD <> LIST [MID]^ then begin
  161.        COUNT := 4*(TOP-MID);
  162.        MID := MID+1;
  163.        Move (LIST [MID], LIST [MID+1], COUNT);
  164.        TOP := TOP+1;
  165.        new (LIST [MID]);
  166.        LIST [MID]^ := A_WORD;
  167.     end;
  168. end;
  169.  
  170. Function DosPath : string;
  171. { **************************************************************************}
  172. { This function extracts the 'PATH =' string from the DOS environment passed
  173. by DOS to the applications program and returns the string, else returns nul.
  174. Restructured for 4.0 Turbo Pascal.}
  175. { **************************************************************************}
  176.   type
  177.     AThing = ^EnvThing;
  178.     EnvThing = array[1..255] of char; {It's a buncha ASCIIZ strings}
  179.   var
  180.     I : word;
  181.     X : integer;
  182.     DosEnvSeg : word;
  183.     DosEnvPtr : AThing;
  184.     DosEnv : EnvThing;
  185.     PathString, EnvString : string;
  186.   begin
  187.     DosEnvSeg := MemW[PrefixSeg:$002c];         {Segment passed by DOS is here}
  188.     I := 0;
  189.     PathString := '';
  190.     repeat                    {DOS always passes a COMSPEC= environment string}
  191.       DosEnvPtr := Ptr(DosEnvSeg,I);
  192.       EnvString := DosEnvPtr^;
  193.       length(EnvString) := pos(chr(0), EnvString)-1;           {ASCIIZ strings}
  194.             {If two consecutive zero bytes then length(EnvString) will be zero}
  195.       I := I+length(EnvString)+1;            {Moves the pointer to next string}
  196.       X := Pos('PATH=',EnvString);
  197.       if X <> 0 then PathString :=
  198.          copy(EnvString, X+5, length(EnvString)-(X+4));
  199.     until (PathString <> '') or (EnvString = '');       {Two zero bytes end it}
  200.     DosPath := PathString;
  201.   end;
  202.  
  203. Function ParsePath (Var PATHSTRING : STRPARAM) : string;
  204. { ***************************************************************************}
  205. { This function returns the first substring of PATHSTRING which is terminated
  206. by the end of the string or by a semicolon. It then alters the input variable
  207. PATHSTRING to remove this part of the string. Thus subsequent calls to
  208. ParsePath will return one part of the parameter string until it is all gone
  209. and will then return a nul string. }
  210. { ***************************************************************************}
  211. var
  212.     x : integer;
  213. begin
  214.     if length (PATHSTRING) = 0 then ParsePath := '' else begin
  215.        x := Pos (';',PATHSTRING);
  216.        if x=0 then begin
  217.           ParsePath := PATHSTRING;
  218.           PATHSTRING := '';
  219.        end
  220.        else begin
  221.           ParsePath := Copy (PATHSTRING, 1, x-1);
  222.           PATHSTRING := Copy (PATHSTRING, x+1, Length (PATHSTRING));
  223.        end;
  224.     end;
  225. end;
  226.  
  227. begin { ******************************************************************}
  228.       { ********                MAIN PROGRAM                   ***********}
  229.       { ******************************************************************}
  230.  
  231. DOCWORDCNT := 0; MISSPELLCNT := 0; ALTDIC := true;
  232. if ParamCount = 0 then begin
  233.     write ('Source file : ');
  234.     readln (SRCNAME);
  235.     ALTDICNAME := '';
  236.     write ('Alternate dictionary : ');
  237.     readln (ALTDICNAME);
  238.     end
  239. else begin
  240.     SRCNAME := ParamStr (1);
  241.     if ParamCount = 1 then ALTDICNAME := '' else ALTDICNAME := ParamStr (2);
  242. end;
  243. if ALTDICNAME = '' then ALTDIC := false;
  244. assign (SRCFILE, SRCNAME);
  245. SetTextBuf (SRCFILE, SBuf);  {Turbo 4.0 setup for I/O buffers}
  246. {$I-} reset (SRCFILE) {$I+};
  247. if IOResult <> 0 then begin
  248.     writeln ('Unable to read the source file. Aborting SPELLER.');
  249.     exit;
  250. end;
  251. if ALTDIC then begin
  252.   assign (ALTDICFILE, ALTDICNAME);
  253.   SetTextBuf (ALTDICFILE, ABuf);    {Turbo 4.0 setup for I/O buffers}
  254.   {$I-} reset (ALTDICFILE) {$I+};
  255.   if IOResult > 0 then begin
  256.     writeln ('Alternate dictionary not found.');
  257.     ALTDIC := false;
  258.   end;
  259. end;
  260. { Find the dictionary file in the current directory on the default
  261.   drive or else go searching for it using the DOS PATH command to
  262.   find drives and directories to search. }
  263. PATHSTRING := DosPath;
  264. MATCH := false;
  265. OPPATH := '';
  266. PREFIX := '';
  267. while MATCH = false do begin
  268.    OPNAME := OPPATH + PREFIX + 'SPELLER.LIS';
  269.    assign (DICFILE, OPNAME);
  270.    SetTextBuf (DICFILE, DBuf);   {Turbo 4.0 setup for I/O buffers}
  271.    {$I-} reset (DICFILE) {$I+};
  272.    x := IOResult;
  273.    MATCH := (x=0);
  274.    OPPATH := ParsePath (PATHSTRING);
  275.    if OPPATH = '' then MATCH := true
  276.    else begin
  277.       if (Pos (':',OPPATH) = Length (OPPATH)) or
  278.          (Pos ('\',OPPATH) = Length (OPPATH)) then PREFIX := ''
  279.       else PREFIX := '\';
  280.    end;
  281. end;
  282. if x<>0 then begin  {I/O error... file not found usually}
  283.    writeln;
  284.    writeln ('Unable to locate the spelling list. Aborting SPELLER.');
  285.    close (SRCFILE);
  286.    if ALTDIC then close (ALTDICFILE);  {Don't close it if it isn't open}
  287.    exit;
  288.    end;
  289. I := Pos ('.',SRCNAME);
  290. if I = 0 then OUTNAME := SRCNAME + '.MIS'
  291.          else OUTNAME := Copy (SRCNAME, 1, I-1) + '.MIS';
  292. assign (MISSFILE, OUTNAME);
  293. {$I-} rewrite (MISSFILE) {$I+};
  294. if IOResult <> 0 then begin
  295.     writeln;
  296.     writeln ('Unable to open the output file. Error code is ',x);
  297.                 {DOS error code for 4.0 Turbo Pascal}
  298.     writeln ('Program terminating.');
  299.     close (SRCFILE);
  300.     close (DICFILE);
  301.     if ALTDIC then close (ALTDICFILE);  {Don't close it if it isn't open}
  302.     exit;
  303.     end;
  304.  
  305. { If no EXIT's were encountered in getting the files opened then we
  306.   continue here with the files all open. }
  307.  
  308. Writeln ('READING  ',SRCNAME);
  309. UNIQUECNT := 1;
  310. New (WORDINDX [1]);
  311. WORDINDX [2] := nil;
  312. WORDINDX [1]^ := '~';
  313. while not eof (SRCFILE) do begin
  314.     GETWORD (SRCFILE, A_WORD);
  315.     if Length (A_WORD) > 1 then begin  {Don't spell check one letter words}
  316.         DOCWORDCNT := DOCWORDCNT + 1;
  317.         ADDUNIQUE (WORDINDX, A_WORD, UNIQUECNT);
  318.     end;
  319. end;
  320. Close (SRCFILE);
  321. {*** Check against dictionary ***}
  322. writeln ('CHECKING SPELLING');
  323. I := 1;
  324. A_WORD := ''; ALTWORD := '';
  325. while I <= UNIQUECNT-1 do begin  {dump the ~ at the end of the list}
  326.     while (A_WORD < WORDINDX [I]^) and not Eof (DICFILE) do
  327.        Readln (DICFILE, A_WORD);
  328.     if A_WORD <> WORDINDX [I]^ then begin
  329.        if ALTDIC then
  330.            while (ALTWORD < WORDINDX[I]^) and not Eof (ALTDICFILE) do
  331.              ReadLn (ALTDICFILE, ALTWORD);
  332.        if ALTWORD <> WORDINDX [I]^ then begin
  333.          Writeln (MISSFILE, WORDINDX [I]^);
  334.          MISSPELLCNT := MISSPELLCNT +1;
  335.        end;
  336.     end;
  337.     I := I + 1;
  338. end { while I <= ... };
  339. Close (DICFILE);
  340. Write (MISSFILE, Chr (26));
  341. Close (MISSFILE);
  342. if ALTDIC then close (ALTDICFILE);  {Don't close it if it aint open}
  343. writeln;
  344. writeln ('Speller done.  Statistics:');
  345. writeln ('    Source file:     ', SRCNAME);
  346. writeln ('    Total words:     ', DOCWORDCNT);
  347. writeln ('    Unique words:    ', UNIQUECNT);
  348. writeln ('    Spelling errors: ', MISSPELLCNT);
  349. End.
  350.